using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using Nini.Config;

namespace Audible
{
    enum PlayMode { Stopped, Verse, Chapter, Indefinitely };
    enum NavMode { Book, Chapter, Verse };
    enum SpeechSpeed { Slow = -3, Normal = 0, Fast = 3 };
    public partial class frmMain : Form
    {
        string inifile = "Audible.ini";
        string dbfile = "kjv.db";
        BibleSQL.BibleSQL bibleSQL;
        MediaPlayer mediaPlayer;
        IniConfigSource configSource = null;
        JoystickManager joy;
        PlayMode playMode;
        NavMode navMode
        {
            get
            {
                if (rbVerse.Checked)
                    return NavMode.Verse;
                else if (rbChapter.Checked)
                    return NavMode.Chapter;
                else
                    return NavMode.Book;
            }
            set
            {
                switch (value)
                {
                    case NavMode.Verse:
                        rbVerse.Checked = true;
                        break;
                    case NavMode.Chapter:
                        rbChapter.Checked = true;
                        break;
                    default:
                        rbBook.Checked = true;
                        break;
                }
                mediaPlayer.PlayNavMode(value);
            }
        }
        bool speakNavigation
        {
            get
            {
                return rbSpeakNavigation.Checked;
            }
            set
            {
                rbSpeakNavigation.Checked = value;
            }
        }
        SpeechSpeed speechSpeed
        {
            get
            {
                if (rbSpeechSlow.Checked)
                    return SpeechSpeed.Slow;
                else if (rbSpeechNormal.Checked)
                    return SpeechSpeed.Normal;
                else
                    return SpeechSpeed.Fast;
            }
            set
            {
                mediaPlayer.VoiceSpeechSpeed = (int)value;
                switch (value)
                {
                    case SpeechSpeed.Slow:
                        rbSpeechSlow.Checked = true;    
                        break;
                    case SpeechSpeed.Normal:
                        rbSpeechNormal.Checked = true;
                        break;
                    case SpeechSpeed.Fast:
                        rbSpeechFast.Checked = true;
                        break;
                }
                mediaPlayer.PlaySpeechSpeed(value);
            }
        }

        public frmMain()
        {
            InitializeComponent();
            // Create BibleSQL object
            bibleSQL = new BibleSQL.BibleSQL(dbfile);
            bibleSQL.PositionChanged += new BibleSQL.BibleSQL.PositionChangedEventDelegate(bibleSQL_PositionChanged);  
            // Create the verse player
            mediaPlayer = new MediaPlayer();
            mediaPlayer.OnSpeechStop += new SpeechStopEvent(mediaPlayer_OnSpeechStop);
            // Load configuration
            LoadConfigs();

            // Init joystick stuff
            joy = new JoystickManager();
            joy.JoystickButtonDown += new JoystickManager.JotstickButtonDownDelegate(joy_JoystickButtonDown);
            joy.JoystickButtonUp += new JoystickManager.JotstickButtonUpDelegate(joy_JoystickButtonUp);
            joy.JoystickMovedHorz += new JoystickManager.JoystickMovedDelegate(joy_JoystickMovedHorz);
            joy.JoystickMovedVirt += new JoystickManager.JoystickMovedDelegate(joy_JoystickMovedVirt);

            InitKeyOverrides(this);
        }

        private void InitKeyOverrides(Control c)
        {
            foreach (Control child in c.Controls)
            {
                InitKeyOverrides(child);
                child.PreviewKeyDown += new PreviewKeyDownEventHandler(child_PreviewKeyDown);
            }
        }

        void child_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            e.IsInputKey = true;
        }

        private void LoadConfigs()
        {
            // Load the configuration source file
            if (System.IO.File.Exists(inifile))
                configSource = new IniConfigSource(inifile);
            else
                configSource = new IniConfigSource();
            // Add main config if not there
            if (configSource.Configs["Main"] == null)
                configSource.AddConfig("Main");
            IConfig config = configSource.Configs["Main"];
            // Load navigation mode
            navMode = StringToNavMode(config.Get("NavMode", "Book"));
            speakNavigation = config.GetBoolean("SpeakNavigation", true);
            // Load bible position
            bibleSQL.Book = config.Get("Book", "Genesis");
            bibleSQL.Chapter = config.GetInt("Chapter", 1);
            bibleSQL.Verse = config.GetInt("Verse", 1);
            // Load speech speed
            speechSpeed = (SpeechSpeed)config.GetInt("SpeechSpeed", (int)SpeechSpeed.Normal);
        }

        private void SaveConfigs()
        {
            IConfig config = configSource.Configs["Main"];
            // Save navigation mode
            config.Set("NavMode", NavModeToString(navMode));
            config.Set("SpeakNavigation", speakNavigation);
            // Save position
            config.Set("Book", bibleSQL.Book);
            config.Set("Chapter", bibleSQL.Chapter);
            config.Set("Verse", bibleSQL.Verse);
            // Save speech speed
            config.Set("SpeechSpeed", (int)speechSpeed);
            // Save ini file
            configSource.Save(inifile);
        }
        private void ShowPosition()
        {
            lbPos.Text = bibleSQL.Position;
        }

        private NavMode StringToNavMode(string p)
        {
            switch (p)
            {
                case "Chapter":
                    return NavMode.Chapter;
                case "Verse":
                    return NavMode.Verse;
                default:
                    return NavMode.Book;
            }
        }

        private object NavModeToString(NavMode navMode)
        {
            switch (navMode)
            {
                case NavMode.Verse:
                    return "Verse";
                case NavMode.Chapter:
                    return "Chapter";
                default:
                    return "Book";
            }
        }

        private void btnKnobBack_Click(object sender, EventArgs e)
        {
            mediaPlayer.PlayTick();
            string s = "";
            switch (navMode)
            {
                case NavMode.Book:
                    bibleSQL.PrevBook();
                    s = bibleSQL.Book;
                    break;
                case NavMode.Chapter:
                    bibleSQL.PrevChapter();
                    s = bibleSQL.Chapter.ToString();
                    break;
                case NavMode.Verse:
                    bibleSQL.PrevVerse();
                    s = bibleSQL.Verse.ToString();
                    break;
            }
            if (speakNavigation)
                mediaPlayer.Speak(s);

        }

        private void btnKnobForward_Click(object sender, EventArgs e)
        {
            mediaPlayer.PlayTick();
            string s = "";
            switch (navMode)
            {
                case NavMode.Book:
                    bibleSQL.NextBook();
                    s = bibleSQL.Book;
                    break;
                case NavMode.Chapter:
                    bibleSQL.NextChapter();
                    s = bibleSQL.Chapter.ToString();
                    break;
                case NavMode.Verse:
                    bibleSQL.NextVerse();
                    s = bibleSQL.Verse.ToString();
                    break;
            }
            if (speakNavigation)
                mediaPlayer.Speak(s);
        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            SaveConfigs();
            joy.Stop();
        }

        private void btnPlay_Click(object sender, EventArgs e)
        {
            playMode = PlayMode.Indefinitely;
            PlayVerse();
        }

        private void btnPlayVerse_Click(object sender, EventArgs e)
        {
            playMode = PlayMode.Verse;
            PlayVerse();
        }

        private void btnPlayChapter_Click(object sender, EventArgs e)
        {
            playMode = PlayMode.Chapter;
            PlayVerse();
        }

        private void btnTellPosition_Click(object sender, EventArgs e)
        {
            playMode = PlayMode.Stopped;
            mediaPlayer.Speak(bibleSQL.Position);
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            playMode = PlayMode.Stopped;
            mediaPlayer.StopSpeech();
        }

        private void rbSpeechSpeed_Click(object sender, EventArgs e)
        {

        }

        private void joy_JoystickButtonDown(object sender, JoystickButtonEvent e)
        {
            System.Console.WriteLine("Button " + e.Button + " down");

            // This function is called from the joystick polling thread stuff needs to be invoked thread-niss-safely
            switch (e.Button)
            {
                case 0:
                    SelectRadioButton(rbBook);
                    break;
                case 1: 
                    SelectRadioButton(rbChapter);
                    break;
                case 2:
                    SelectRadioButton(rbVerse);
                    break;
                case 3:
                    SelectRadioButton(rbSpeechSlow);
                    break;
                case 4:
                    SelectRadioButton(rbSpeechNormal);
                    break;
                case 5:
                    SelectRadioButton(rbSpeechFast);
                    break;
                case 6:
                    ClickButton(btnStop);
                    break;
                case 7:
                    float axisPos = joy.GetVAxisPos() * 100;
                    switch ((int)axisPos)
                    {
                        case 0:
                            ClickButton(btnPlay);
                            break;
                        case 100:
                            ClickButton(btnPlayVerse);
                            break;
                        default:
                            ClickButton(btnPlayChapter);
                            break;
                    }
                    break;
                case 8:
                    CloseForm(this);
                    break;
                case 9:
                    ClickButton(btnTellPosition);
                    break;
            }
        }

        private void joy_JoystickButtonUp(object sender, JoystickButtonEvent e)
        {
            System.Console.WriteLine("Button " + e.Button + " up");
        }

        void joy_JoystickMovedHorz(object sender, JoystickMovedEvent e)
        {
            System.Console.WriteLine("Hort Pos " + e.AxisPos);
            if (e.AxisPos < 0.5)
                ClickButton(btnKnobBack);
            else if (e.AxisPos > 0.5)
                ClickButton(btnKnobForward);
        }

        void joy_JoystickMovedVirt(object sender, JoystickMovedEvent e)
        {
        }

        delegate void CloseFormCallback(Form form);
        private void CloseForm(Form form)
        {
            if (form.InvokeRequired)
            {
                CloseFormCallback d = new CloseFormCallback(CloseForm);
                this.Invoke(d, new object[] { form });
            }
            else
                form.Close();
        }

        delegate void SelectRadioButtonCallback(RadioButton rb);
        private void SelectRadioButton(RadioButton rb)
        {
            // Select radio button from another thread
            if (rb.InvokeRequired)
            {
                SelectRadioButtonCallback d = new SelectRadioButtonCallback(SelectRadioButton);
                this.Invoke(d, new object[] { rb });
            }
            else
                rb.Checked = true;
        }

        delegate void ClickButtonCallback(Button btn);
        private void ClickButton(Button btn)
        {
            // Click button from another thread
            if (btn.InvokeRequired)
            {
                ClickButtonCallback d = new ClickButtonCallback(ClickButton);
                this.Invoke(d, new object[] { btn });
            }
            else    
                btn.PerformClick();         
        }

        private void rbSpeechSpeed_CheckedChanged(object sender, EventArgs e)
        {
            speechSpeed = speechSpeed;
        }

        private void PlayVerse()
        {
            mediaPlayer.Speak(bibleSQL.Scripture);
        }

        private void mediaPlayer_OnSpeechStop()
        {
            System.Console.WriteLine("speech stopped");
            switch (playMode)
            {
                case PlayMode.Verse:
                    break;
                case PlayMode.Chapter:
                    bibleSQL.NextVerse();
                    if (bibleSQL.Verse != 1)
                        PlayVerse();
                    else
                        playMode = PlayMode.Stopped;
                    break;
                case PlayMode.Indefinitely:
                    bibleSQL.NextVerse();
                    PlayVerse();
                    break;
            }
        }

        private void bibleSQL_PositionChanged(object sender, EventArgs e)
        {
            ShowPosition();
        }

        private void rbNavMode_CheckedChanged(object sender, EventArgs e)
        {
            navMode = navMode;
        }

        private void btnSetKeyBindings_Click(object sender, EventArgs e)
        {
            KeyBindingsForm f = new KeyBindingsForm();
            f.ShowDialog();
        }

        /// <summary>
        /// This function is useful to simulate Key presses to the window with focus.
        /// </summary>
        /// <param name="bVk">Specifies a virtual-key code. The code must be a value in the range 1 to 254.</param>
        /// <param name="bScan">Specifies a hardware scan code for the key.</param>
        /// <param name="dwFlags"> Specifies various aspects of function operation. This parameter can be one or more of the following values.
        ///                         <code>KEYEVENTF_EXTENDEDKEY</code> or <code>KEYEVENTF_KEYUP</code>
        ///                         If specified, the key is being released. If not specified, the key is being depressed.</param>
        /// <param name="dwExtraInfo">Specifies an additional value associated with the key stroke</param>
        [DllImport("user32.dll")]
        static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);

        private void PressNumLock()
        {
            const byte NUMLOCK_KEY = 0x90;
            const int KEYEVENTF_EXTENDEDKEY = 0x1;
            const int KEYEVENTF_KEYUP = 0x2;
            keybd_event(NUMLOCK_KEY, 0x45, KEYEVENTF_EXTENDEDKEY, 0);
            keybd_event(NUMLOCK_KEY, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            e.Handled = true;

            // toggle num lock if need be
            if (!Console.NumberLock)
                PressNumLock();

            // left & right
            if (e.KeyCode == Keys.NumPad4)
                ClickButton(btnKnobBack);
            else if (e.KeyCode == Keys.NumPad6)
                ClickButton(btnKnobForward);

            // play buttons
            else if (e.KeyCode == Keys.NumPad5)
                ClickButton(btnPlay);
            else if (e.KeyCode == Keys.NumPad8)
                ClickButton(btnPlayVerse);
            else if (e.KeyCode == Keys.NumPad2)
                ClickButton(btnPlayChapter);

            // tell pos & stop
            else if (e.KeyCode == Keys.NumPad7)
                ClickButton(btnTellPosition);
            else if (e.KeyCode == Keys.NumPad9)
                ClickButton(btnStop);

            // speed
            else if (e.KeyCode == Keys.Insert)
                SelectRadioButton(rbSpeechSlow);
            else if (e.KeyCode == Keys.Home)
                SelectRadioButton(rbSpeechNormal);
            else if (e.KeyCode == Keys.PageUp)
                SelectRadioButton(rbSpeechFast);

            // mode
            else if (e.KeyCode == Keys.Delete)
                SelectRadioButton(rbBook);
            else if (e.KeyCode == Keys.End)
                SelectRadioButton(rbChapter);
            else if (e.KeyCode == Keys.PageDown)
                SelectRadioButton(rbVerse);

            // quit
            else if (e.KeyCode == Keys.Escape)
                Close();
        }
    }
}